﻿

namespace TYG.Client
{
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading.Tasks;
    using DotNetty.Codecs;
    using DotNetty.Handlers.Tls;
    using DotNetty.Transport.Bootstrapping;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Sockets;
    using TYG.Common;
    using DotNetty.Codecs.Protobuf;
    using DotNetty.Buffers;
    using Google.Protobuf;
    using System.Collections.Generic;
    using System.Linq;
    using TYG.Proto;

    public class ClientSdk<T>
    {

       

        private static ClientSdk<T> sdk;
        private List<T> reponsoseData { get; set; } = new List<T>();
      
        public IEnumerable<T> Get() {
            return reponsoseData;
        }
        public T Pop(Func<T,bool> func) {
            var data= reponsoseData.FirstOrDefault(func);
            reponsoseData.Remove(data);
            return data;
        }

        public void Clear()
        {
            reponsoseData = new List<T>();
        }

        public void Flush() {
            reponsoseData = new List<T>();
        }
        public void Push(T t)
        {
            reponsoseData.Add(t);
        }
        public string Host { get; set; } = "127.0.0.1";
        public int Port { get; set; } = 8999;

        private static IChannel bootstrapChannel;

        public int MsgCount() {
            return reponsoseData.Count();
        }
        public static ClientSdk<T> Instance { get {
                if (sdk==null)
                {
                    sdk = new ClientSdk<T>();
                }
                return sdk;
            } }
        private  async Task RunClientAsync(MessageParser message)
        {
            //TianYou.SetConsoleLogger();
            var STRING_ENCODER = new ProtobufEncoder();
            var STRING_DECODER = new ProtobufDecoder(message);
            var SERVER_HANDLER = new SecureChatClientHandler();

   


            var group = new MultithreadEventLoopGroup();

            X509Certificate2 cert = null;
            string targetHost = null;
            if (TY.IsSsl)
            {
                cert = new X509Certificate2(Path.Combine(TY.ProcessDirectory, "thorx.cn.pem"), "thorx.cn.key");
                targetHost = cert.GetNameInfo(X509NameType.DnsName, false);
            }
            try
            {
                var bootstrap = new Bootstrap();
                bootstrap
                    .Group(group)
                    .Channel<TcpSocketChannel>()
                    .Option(ChannelOption.TcpNodelay, true)
                    .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;

                        if (cert != null)
                        {
                            pipeline.AddLast(new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(targetHost)));
                        }
                        //  pipeline.AddLast(new DelimiterBasedFrameDecoder(8192, Delimiters.LineDelimiter()));
                        //pipeline.AddLast(STRING_ENCODER);
                        //pipeline.AddLast(SERVER_HANDLER);
                        pipeline.AddLast(STRING_ENCODER,  SERVER_HANDLER);
                    }));

                 bootstrapChannel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(Host), port:Port));

  
            }
            finally
            {
                //group.ShutdownGracefullyAsync().Wait(1000);
            }
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendMessageAsync(IMessage message,MessageParser parser) {
            if (bootstrapChannel == null)
            {
                await RunClientAsync(parser);
            }
            var inputBuffer = Unpooled.WrappedBuffer(message.ToByteArray());
            await bootstrapChannel.WriteAndFlushAsync(inputBuffer);
        }
        /// <summary>
        /// 连接TCP
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task ConnectTCPAsync(MessageParser message) {
            await RunClientAsync(message);
        }
    }

    public class SecureChatClientHandler: SimpleChannelInboundHandler<IByteBuffer>
    {
        protected override void ChannelRead0(IChannelHandlerContext contex, IByteBuffer msg)
        {
        
            if (Convert(msg, TankModel.Parser)!=null)
            {
                var p = Convert(msg, TankModel.Parser);
                Console.WriteLine(p);
            }
            else if (Convert(msg, SendFrameRequest.Parser)!=null)
            {
                var p = Convert(msg, SendFrameRequest.Parser);
                Console.WriteLine(p);
            }

        }
        public override Task CloseAsync(IChannelHandlerContext context)
        {
            return base.CloseAsync(context);
        }

        public override void ExceptionCaught(IChannelHandlerContext contex, Exception e)
        {
            Console.WriteLine(DateTime.Now.Millisecond);
            Console.WriteLine(e.StackTrace);
            contex.CloseAsync();
        }

        private IMessage Convert(IByteBuffer message, MessageParser messageParser)
        {
            int length = message.ReadableBytes;
            if (length <= 0)
            {
                return null;
            }

            Stream inputStream = null;
            try
            {
                CodedInputStream codedInputStream;
                if (message.IoBufferCount == 1)
                {
                    ArraySegment<byte> bytes = message.GetIoBuffer(message.ReaderIndex, length);
                    codedInputStream = new CodedInputStream(bytes.Array, bytes.Offset, length);
                }
                else
                {
                    inputStream = new ReadOnlyByteBufferStream(message, false);
                    codedInputStream = new CodedInputStream(inputStream);
                }

                //
                // Note that we do not dispose the input stream because there is no input stream attached. 
                // Ideally, it should be disposed. BUT if it is disposed, a null reference exception is 
                // thrown because CodedInputStream flag leaveOpen is set to false for direct byte array reads,
                // when it is disposed the input stream is null.
                // 
                // In this case it is ok because the CodedInputStream does not own the byte data.
                //
                IMessage decoded = messageParser.ParseFrom(codedInputStream);
                return decoded;
            }
            catch (Exception exception)
            {
                throw new CodecException(exception);
            }
            finally
            {
                inputStream?.Dispose();
            }

        }
    }

}
